/*******************************************************************************
 * Copyright 2011 Mile Orešković
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.mileoresko.hierarchism.testhsms.course;

import com.mileoresko.hierarchism.HSMEvent;
import com.mileoresko.hierarchism.HSMState;
import com.mileoresko.hierarchism.HSMTerminalState;
import com.mileoresko.hierarchism.HierarchicalStateMachine;
import com.mileoresko.hierarchism.exceptions.HSMCommonTransitionDefinitionException;
import com.mileoresko.hierarchism.exceptions.HSMDefinitionException;
import com.mileoresko.hierarchism.exceptions.HSMDuplicateTransitionException;
import com.mileoresko.hierarchism.exceptions.HSMInternalTransitionDefinitionException;
import com.mileoresko.hierarchism.exceptions.HSMPseudoChoiceDefinitionException;
import com.mileoresko.hierarchism.testhsms.course.Enrollment.EnrollmentEvents;

public class Course extends HierarchicalStateMachine {

    public class CourseEnrollmentState extends CourseState {
        Enrollment enrollment;

        protected CourseEnrollmentState(final String label) {
            super(label);
        }

        protected void defineNestedHsms() {
            enrollment = new Enrollment(label, 100, 20);
            enrollment.addEventMapping(CourseEvents.CANCEL, EnrollmentEvents.CANCEL);
            enrollment.addEventMapping(CourseEvents.TERM_START, EnrollmentEvents.CLOSE);
            addNestedHsm(enrollment);
        }

        protected void entryAction(final Object arg) {
            smLog("Starting enrollment!");
        }

        public Enrollment getNestedEnrollmentHsm() {
            return enrollment;
        }
    }

    public static class CourseEvent extends HSMEvent {
        protected CourseEvent(final String label) {
            super(label);
        }
    }

    public static class CourseEvents {
        public static final CourseEvent TERM_START = new CourseEvent("TermStart");
        public static final CourseEvent CANCEL = new CourseEvent("CancelCourse");
        public static final CourseEvent LECTURES_OVER = new CourseEvent("LecturesOver");
        public static final CourseEvent EXAMS_OVER = new CourseEvent("ExamsOver");
    }

    public class CourseState extends HSMState {
        protected CourseState(final String label) {
            super(Course.this, label);
        }

        /**
         * A default empty defineNestedHsms implementation is provided for convenience.
         * Override where needed.
         */
        protected void defineNestedHsms() {

        }

        /**
         * A default empty entryAction implementation is provided for convenience.
         * Override where needed.
         */
        protected void entryAction(final Object arg) {

        }

        /**
         * A default empty exitAction implementation is provided for convenience.
         * Override where needed.
         */
        protected void exitAction(final Object arg) {

        }
    }

    public class CourseStates {
        public final CourseState LECTURES = new CourseState("Lectures") {
            protected void entryAction(final Object arg) {
                smLog("Starting lectures!");
            }
        };

        public final CourseEnrollmentState ENROLLMENT = new CourseEnrollmentState("Enrollment");

        public final CourseState FINAL_EXAMS = new CourseState("FinalExams") {
            protected void entryAction(final Object arg) {
                smLog("Starting final exams!");
            }
        };

        public final HSMTerminalState COMPLETE = new HSMTerminalState(Course.this, "Complete") {
            protected void entryAction(final Object arg) {
                smLog("Course complete!");
            }
        };

        public final HSMTerminalState CANCELLED = new HSMTerminalState(Course.this, "CourseCancelled") {
            protected void entryAction(final Object arg) {
                smLog("Course cancelled!");
            }
        };
    }

    private final int maxGroupSize;
    private final int maxWaitingListSize;
    private final CourseStates states;

    public Course(final String label, final int maxGroupSize, final int maxWaitingListSize) {
        super(label);
        this.maxGroupSize = maxGroupSize;
        this.maxWaitingListSize = maxWaitingListSize;
        states = new CourseStates();
        try {
            create();
        } catch (final HSMDefinitionException exc) {
            exc.printStackTrace();
            System.exit(-1);
        }
    }

    protected void defineTransitions() throws HSMDuplicateTransitionException,
            HSMInternalTransitionDefinitionException, HSMPseudoChoiceDefinitionException,
            HSMCommonTransitionDefinitionException {
        addCommonTransition(states.ENROLLMENT, CourseEvents.TERM_START, states.LECTURES, null);
        addCommonTransition(states.ENROLLMENT, CourseEvents.CANCEL, states.CANCELLED, null);

        addCommonTransition(states.LECTURES, CourseEvents.LECTURES_OVER, states.FINAL_EXAMS, null);
        addCommonTransition(states.LECTURES, CourseEvents.CANCEL, states.CANCELLED, null);

        addCommonTransition(states.FINAL_EXAMS, CourseEvents.EXAMS_OVER, states.COMPLETE, null);
        addCommonTransition(states.FINAL_EXAMS, CourseEvents.CANCEL, states.CANCELLED, null);
    }

    protected HSMState getInitialState() {
        return states.ENROLLMENT;
    }

    public CourseStates states() {
        return states;
    }
}
